﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AAA.Algorithm.Data;

namespace AAA.Algorithm.Cluster.SOM
{
    public class SOMMapNode : AbstractMapNode
    {
        private INeuron _WeightNeuron = new Neuron();
        private int _iPreviousIteration = 0;
        private float _fLearningRate = 1;
        private float _fLearningRateDes = 1;

        public float LearningRateDes
        {
            get { return _fLearningRateDes; }
            set { _fLearningRateDes = value; }
        }

        public float LearningRate
        {
            get { return _fLearningRate; }
            set { _fLearningRate = value; }
        }

        public override float CalculateSimilarity(INeuron sourceNeuron)
        {
            float fSimilarity = 0;
            try
            {
                SOMSimilarity somSimilarity = new SOMSimilarity();
                return somSimilarity.Similarity(sourceNeuron, _WeightNeuron);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
            return fSimilarity;
        }

        public void UpdateWeight(INeuron sourceNeuron, int intDistance, int intR)
        {
            try
            {
                if (_iPreviousIteration != CurrentIteration) {
                    LearningRate = LearningRate * LearningRateDes;
                    _iPreviousIteration = CurrentIteration;
                }

                for (int i = 0; i < sourceNeuron.FactorCount; i++)
                {
                    float var_Weight =  LearningRate * (sourceNeuron.GetFactor(i) - _WeightNeuron.GetFactor(i)) * CallNeighborhood(sourceNeuron, intR);
                    _WeightNeuron.SetFactor(i, _WeightNeuron.GetFactor(i) + var_Weight);
                }
            }
            catch (Exception e) {
                Console.WriteLine(e.Message.ToString());
            }
        }

        public void SetInitialWeight(INeuron Weights) {
            _WeightNeuron = Weights;
        }

        private float CallNeighborhood(INeuron sourceNeuron, int intR)
        {
            float fResult = float.NaN;
            float fSum = 0;

            for (int i = 0; i < sourceNeuron.FactorCount; i++)
                fSum += (float)Math.Pow(sourceNeuron.GetFactor(i) - _WeightNeuron.GetFactor(i), 2);
            fSum = (float)Math.Sqrt(fSum);

            try { 
                fResult = (float)Math.Exp((0 - fSum)/intR);
            }
            catch (Exception e) {
                Console.WriteLine(e.Message.ToString());
            }
            return fResult;
        }

        public INeuron GetWeights() {
            return _WeightNeuron;
        }
    }
}
